#include <memory>
#include <iostream>
#include <brpc/server.h>
#include <butil/logging.h>

// common文件夹下
#include "data_es.hpp"
#include "data_redis.hpp"
#include "mysql_user.hpp"
#include "etcder.hpp"
#include "uuid.hpp"
#include "channel.hpp"
#include "logger.hpp"
#include "dmser.hpp"
#include "user.pb.h"
#include "base.pb.h"
#include "file.pb.h"

#include "user.hxx"
namespace bite_im
{
    // 实现用户模块功能

    // 用户模块服务类
    class UserServiceImpl : public bite_im::UserService
    {
    private:
        // 部分业务中的差错检验函数
        bool check_nickname(const std::string &nickname)
        {
            return nickname.size() <= 21 ? true : false;
        }
        bool check_password(const std::string &password)
        {
            if (password.size() < 6 || password.size() > 15)
            {
                LOG_ERROR("密码长度不合法：{}-{}", password, password.size());
                return false;
            }
            for (int i = 0; i < password.size(); i++)
            {
                if (!((password[i] > 'a' && password[i] < 'z') ||
                      (password[i] > 'A' && password[i] < 'Z') ||
                      (password[i] > '0' && password[i] < '9') ||
                      password[i] == '_' || password[i] == '-'))
                {
                    LOG_ERROR("密码字符不合法：{}", password);
                    return false;
                }
            }
            return true;
        }
        bool check_phone(const std::string &phone)
        {
            if (phone.size() != 11 || phone[0] != '1' || phone[1] < '3' || phone[1] > '9')
            {
                return false;
            }
            for (int i = 2; i < 11; i++)
            {
                if (phone[i] < '0' || phone[i] > '9')
                {
                    return false;
                }
            }
            return true;
        }

    public:
        using ptr = std::shared_ptr<UserServiceImpl>;
        UserServiceImpl(const DMSClient::ptr dms_client,
                        const ElasticPtr &es_client,
                        const DBPtr &mysql_client,
                        const RedisPtr &redis_client,
                        const ServiceManager::ptr &service_manager,
                        const std::string &file_service_name)
            : _dms_client(dms_client),
              _es_user(std::make_shared<ESUser>(es_client)),
              _mysql_user(std::make_shared<UserTable>(mysql_client)),
              _redis_session(std::make_shared<Session>(redis_client)),
              _redis_status(std::make_shared<Status>(redis_client)),
              _redis_codes(std::make_shared<Codes>(redis_client)),
              _service_manager(service_manager),
              _file_service_name(file_service_name) {}

        ~UserServiceImpl() {}

        /* --------------------------------------------- 业务函数----------------------------------------------------*/

        void UserRegister(google::protobuf::RpcController *controller,
                          const ::bite_im::UserRegisterReq *request,
                          ::bite_im::UserRegisterRsp *response,
                          ::google::protobuf::Closure *done)
        {
            // 错误函数---lambda
            auto err_response_func = [this, response](const std::string &request_id,
                                                      const std::string &errmsg) -> void
            {
                response->set_request_id(request_id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            // 管理rpc对象
            brpc::ClosureGuard rpc_guard(done);
            std::string nickname = request->nickname();
            std::string password = request->password();

            // 1.进行合法性检验
            // 检测昵称、密码长度是否合法
            if (check_nickname(nickname) == false)
            {
                return err_response_func(request->request_id(), "用户名格式不合法");
            }
            if (check_password(password) == false)
            {
                return err_response_func(request->request_id(), "密码不合法");
            }

            // 2.存储数据到MySQL数据库
            // 查询是否该用户名存储在MySQL数据库中
            auto user = _mysql_user->select_by_nickname(nickname);
            if (user) // 数据库中存在该字段
            {
                LOG_ERROR("用户名称[{}]已存在，请求ID[{}]", nickname, request->request_id());
                return err_response_func(request->request_id(), "用户名已存在，请换一个ID");
            }
            std::string uuid = UUID::uuid();
            UserPtr new_user = std::make_shared<User>(uuid, nickname, password);
            // 判断是否添加进入MySQL成功
            if (_mysql_user->insert(new_user) == false)
            {
                LOG_ERROR("添加用户名称[{}]进入MySQL数据库失败，请求ID[{}]", nickname, request->request_id());
                return err_response_func(request->request_id(), "用户名添加失败，请重试");
            }
            LOG_INFO("添加用户名称[{}]进入MySQL数据库成功，请求ID[{}]", nickname, request->request_id());

            // 3.存储数据到es中
            if (_es_user->appendData(uuid, "", nickname, "", "") == false)
            {
                LOG_ERROR("添加用户到ES搜索引擎新失败，请求ID[{}]", request->request_id());

                return err_response_func(request->request_id(), "用户名添加失败，请重试");
            }
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

        void UserLogin(google::protobuf::RpcController *controller,
                       const ::bite_im::UserLoginReq *request,
                       ::bite_im::UserLoginRsp *response,
                       ::google::protobuf::Closure *done)
        {
            // 错误函数---lambda
            auto err_response_func = [this, response](const std::string &request_id,
                                                      const std::string &errmsg) -> void
            {
                response->set_request_id(request_id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                // 无法登陆不用设置保存session_id
                return;
            };
            // 管理rpc对象
            brpc::ClosureGuard rpc_guard(done);
            std::string nickname = request->nickname();
            std::string password = request->password();
            auto user = _mysql_user->select_by_nickname(nickname);
            // 差错检验
            if (!user)
            {
                LOG_ERROR("用户名称[{}]不存在，请求ID[{}]", nickname, request->request_id());
                return err_response_func(request->request_id(), "用户名不存在，无法登陆");
            }
            if (user->password() != password)
            {
                LOG_ERROR("用户[{}]密码错误，请求ID[{}]", nickname, request->request_id());
                return err_response_func(request->request_id(), "密码有误，无法登陆");
            }
            // 判断是否登录了
            if (_redis_status->exists(user->user_id()) == true)
            {
                LOG_ERROR("用户[{}]其他地方登录，请求ID[{}]", nickname, request->request_id());
                return err_response_func(request->request_id(), "用户已在其他地方登录");

                // 如果是有登录过的，我们也可以实现允许其他节点登录，但是需要退出当前节点
                // 这时我们就不返回错误，而是在响应中维护一个标志

                // response->set_ifLoad(true);
                // 并且不用在redis中添加user_id了
            }
            // 用户新增后添加到redis中缓存
            std::string session_id = UUID::uuid();
            _redis_session->append(session_id, user->user_id());
            _redis_status->append(user->user_id());
            // 组织响应
            response->set_request_id(request->request_id());
            response->set_success(true);
            response->set_login_session_id(session_id);
            // response->set_ifLoad(false);
        }

        void GetPhoneVerifyCode(google::protobuf::RpcController *controller,
                                const ::bite_im::PhoneVerifyCodeReq *request,
                                ::bite_im::PhoneVerifyCodeRsp *response,
                                ::google::protobuf::Closure *done)
        {
            // 错误函数---lambda
            auto err_response_func = [this, response](const std::string &request_id,
                                                      const std::string &errmsg) -> void
            {
                response->set_request_id(request_id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                // 无法登陆不用设置保存code_id
                return;
            };
            // 管理rpc对象
            brpc::ClosureGuard rpc_guard(done);
            std::string phone = request->phone_number();
            // 1.差错检测
            if (check_phone(phone) == false)
            {
                LOG_ERROR("手机号码[{}]格式有误，请求ID[{}]", phone, request->request_id());
                return err_response_func(request->request_id(), "手机号码格式有误");
            }
            // 2.生成验证码和id
            std::string code_id = UUID::uuid();
            std::string code = VerifyCode::code();
            // 3.基于SDK平台发送验证码
            if (_dms_client->sendCode(phone, code) == false)
            {
                LOG_ERROR("向手机号码[{}]发送短信失败，请求ID[{}]", phone, request->request_id());
                return err_response_func(request->request_id(), "发送验证码失败");
            }
            // 4.将验证码添加到redis中
            _redis_codes->append(code_id, code);
            // 5.组织响应
            response->set_request_id(request->request_id());
            response->set_success(true);
            response->set_verify_code_id(code_id);
        }

        void PhoneRegister(google::protobuf::RpcController *controller,
                           const ::bite_im::PhoneRegisterReq *request,
                           ::bite_im::PhoneRegisterRsp *response,
                           ::google::protobuf::Closure *done)
        {
            // 错误函数---lambda
            auto err_response_func = [this, response](const std::string &request_id,
                                                      const std::string &errmsg) -> void
            {
                response->set_request_id(request_id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            // 管理rpc对象
            brpc::ClosureGuard rpc_guard(done);
            std::string phone = request->phone_number();
            // 1.差错检测
            if (check_phone(phone) == false)
            {
                LOG_ERROR("手机号码[{}]格式有误，请求ID[{}]", phone, request->request_id());
                return err_response_func(request->request_id(), "手机号码格式有误");
            }
            // 2.获取验证码和id并检验
            std::string code_id = request->verify_code_id();
            std::string code = request->verify_code();
            std::string redis_code = *(_redis_codes->code(code_id));
            if (redis_code != code)
            {
                // 如果报错可能是 *(_redis_codes->code(code_id));解引用的问题
                LOG_ERROR("验证码[{}]-[{}]不匹配，请求ID[{}]", redis_code, code, request->request_id());
                return err_response_func(request->request_id(), "验证码错误");
            }
            // 注意验证码通过之后要删除
            _redis_codes->remove(code_id);
            // 3.判断手机号是否注册过
            auto user = _mysql_user->select_by_phone(phone);
            if (user) // 表示注册过了
            {
                LOG_ERROR("手机号码[{}]已注册，请求ID[{}]", phone, request->request_id());
                return err_response_func(request->request_id(), "手机号码已注册");
            }
            // 4.对该手机号进行注册用户，并添加到数据库中
            std::string user_id = UUID::uuid();
            auto new_user = std::make_shared<User>(user_id, phone);
            if (_mysql_user->insert(new_user) == false)
            {
                LOG_ERROR("添加手机号注册用户[{}]进入MySQL数据库失败，请求ID[{}]", phone, request->request_id());
                return err_response_func(request->request_id(), "手机号注册失败，请重试");
            }
            // 5.添加到es搜索引擎中
            if (_es_user->appendData(user_id, phone, user_id, "", "") == false)
            {
                LOG_ERROR("添加手机号到ES搜索引擎新失败，请求ID[{}]", request->request_id());
                return err_response_func(request->request_id(), "手机号注册失败，请重试");
            }
            // 组织响应
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

        void PhoneLogin(google::protobuf::RpcController *controller,
                        const ::bite_im::PhoneLoginReq *request,
                        ::bite_im::PhoneLoginRsp *response,
                        ::google::protobuf::Closure *done)
        {
            // 错误函数---lambda
            auto err_response_func = [this, response](const std::string &request_id,
                                                      const std::string &errmsg) -> void
            {
                response->set_request_id(request_id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                // 无法登陆不用设置保存code_id
                return;
            };
            // 管理rpc对象
            brpc::ClosureGuard rpc_guard(done);
            std::string phone = request->phone_number();
            // 1.差错检测
            if (check_phone(phone) == false)
            {
                LOG_ERROR("手机号码[{}]格式有误，请求ID[{}]", phone, request->request_id());
                return err_response_func(request->request_id(), "手机号码格式有误");
            }
            // 2.检测手机号是否存在
            auto user = _mysql_user->select_by_phone(phone);
            if (!user) // 表示未注册
            {
                LOG_ERROR("手机号码[{}]未注册，请求ID[{}]", phone, request->request_id());
                return err_response_func(request->request_id(), "手机号码未注册");
            }
            // 3.判断验证码是否正确
            std::string code_id = request->verify_code_id();
            std::string code = request->verify_code();
            std::string redis_code = *(_redis_codes->code(code_id));
            if (redis_code != code)
            {
                // 如果报错可能是 *(_redis_codes->code(code_id));解引用的问题
                LOG_ERROR("验证码[{}]-[{}]不匹配，请求ID[{}]", redis_code, code, request->request_id());
                return err_response_func(request->request_id(), "验证码错误");
            }
            // 注意验证码通过之后要删除
            _redis_codes->remove(code_id);
            // 4.判断是否登录了
            if (_redis_status->exists(user->user_id()) == true)
            {
                LOG_ERROR("手机号用户[{}]其他地方登录，请求ID[{}]", phone, request->request_id());
                return err_response_func(request->request_id(), "手机号用户已在其他地方登录");
            }
            // 5.用户新增后添加到redis中缓存
            std::string session_id = UUID::uuid();
            _redis_session->append(session_id, user->user_id());
            _redis_status->append(user->user_id());
            // 组织响应
            response->set_request_id(request->request_id());
            response->set_success(true);
            response->set_login_session_id(session_id);
        }

        void GetUserInfo(google::protobuf::RpcController *controller,
                         const ::bite_im::GetUserInfoReq *request,
                         ::bite_im::GetUserInfoRsp *response,
                         ::google::protobuf::Closure *done)
        {
            // 错误函数---lambda
            auto err_response_func = [this, response](const std::string &request_id,
                                                      const std::string &errmsg) -> void
            {
                response->set_request_id(request_id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                // 无法登陆不用设置保存code_id
                return;
            };
            // 管理rpc对象
            brpc::ClosureGuard rpc_guard(done);
            // 1.检测用户是否存在
            std::string user_id = request->user_id();
            auto user = _mysql_user->select_by_id(user_id);
            if (!user) // 表示不存在该用户
            {
                LOG_ERROR("用户[{}]不存在，请求ID[{}]", user_id, request->request_id());
                return err_response_func(request->request_id(), "提供用户id无法找到用户");
            }
            // 2.通过查询到的用户信息，设置到响应中发送给客户端
            UserInfo *info = response->mutable_user_info(); // 获取指针，直接对指针进行操作
            info->set_user_id(user->user_id());
            info->set_nickname(user->nickname());
            info->set_description(user->description());
            info->set_phone(user->phone());

            // 3.从磁盘读取文件头像信息
            if (user->avatar_id().empty() == false) // 用户头像不为空
            {
                // 从服务信道管理模块获取文件存储子服务的service
                auto channel = _service_manager->choose(_file_service_name);
                if (!channel) // 服务不存在
                {
                    LOG_ERROR("文件子服务[{}]节点未找到，请求ID[{}]", _file_service_name, request->request_id());
                    return err_response_func(request->request_id(), "未找到文件子服务管理节点");
                }
                // 获取到文件子服务操作对象
                bite_im::FileService_Stub stub(channel.get());
                bite_im::GetSingleFileReq req;
                // 设置文件子服务的请求
                req.set_request_id(request->request_id());
                req.set_file_id(user->avatar_id());
                bite_im::GetSingleFileRsp resp;
                // 获取一个rpc操作对象
                brpc::Controller cntl;
                // stub调用我们实现的服务类ipml的接口
                stub.GetSingleFile(&cntl, &req, &resp, nullptr);
                if (cntl.Failed() == true || resp.success() == false)
                {
                    LOG_ERROR("文件子服务调用失败，请求ID[{}]，原因[{}]", request->request_id(), cntl.ErrorText());
                    return err_response_func(request->request_id(), "文件子服务调用失败");
                }

                // 获取到响应的数据resp->file_data().file_content()设置文件头像
                info->set_avatar(resp.file_data().file_content());
            }

            // 3.组织响应
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

        void GetMultiUserInfo(google::protobuf::RpcController *controller,
                              const ::bite_im::GetMultiUserInfoReq *request,
                              ::bite_im::GetMultiUserInfoRsp *response,
                              ::google::protobuf::Closure *done)
        {
            // 错误函数---lambda
            auto err_response_func = [this, response](const std::string &request_id,
                                                      const std::string &errmsg) -> void
            {
                response->set_request_id(request_id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                // 无法登陆不用设置保存code_id
                return;
            };
            // 管理rpc对象
            brpc::ClosureGuard rpc_guard(done);

            // 1.从列表请求中获取多个id
            std::vector<std::string> user_id_list;
            for (int i = 0; i < request->users_id_size(); i++)
            {
                user_id_list.push_back(request->users_id(i));
            }

            // 2.从数据库中进行批量数据查询
            std::vector<UserPtr> users = _mysql_user->select_multi_users(user_id_list);

            if (users.size() != request->users_id_size())
            {
                LOG_ERROR("数据库查找用户信息不一致[{}]-[{}]，请求ID[{}]", users.size(),
                          request->users_id_size(), request->request_id());
                return err_response_func(request->request_id(), "从数据库查找的用户信息数量不一致");
            }

            // 3.通过文件子服务获取头像列表
            // 从服务信道管理模块获取文件存储子服务的service
            auto channel = _service_manager->choose(_file_service_name);
            if (!channel) // 服务不存在
            {
                LOG_ERROR("文件子服务[{}]节点未找到，请求ID[{}]", _file_service_name, request->request_id());
                return err_response_func(request->request_id(), "未找到文件子服务管理节点");
            }
            bite_im::FileService_Stub stub(channel.get());
            bite_im::GetMultiFileReq req;
            req.set_request_id(request->request_id());
            // 从users里面查找哪些是有头像数据的并加入到req中
            for (auto &user : users)
            {
                if (user->avatar_id().empty() == false)
                {
                    req.add_file_id_list(user->avatar_id());
                }
            }
            bite_im::GetMultiFileRsp resp;
            // 获取一个rpc操作对象
            brpc::Controller cntl; // ??
            // stub调用我们实现的服务类ipml的接口
            stub.GetMultiFile(&cntl, &req, &resp, nullptr);
            if (cntl.Failed() == true || resp.success() == false)
            {
                LOG_ERROR("文件子服务调用失败，请求ID[{}]，原因[{}]", request->request_id(), cntl.ErrorText());
                return err_response_func(request->request_id(), "文件子服务调用失败");
            }

            // 有无敌大bug！！！！
            // // 通过文件子服务的resp获取到的 avatar_id to avatar头像数据 映射
            // auto file_map = resp.file_data();                // 是否用mutablefile_data() ？？？
            // auto user_map = *(response->mutable_users_info()); // 可以先解引用吗？？

            // 4.组织响应
            // 我们可以从users中获取一个个user
            for (auto &user : users)
            {
                auto user_map = response->mutable_users_info(); // 本次请求要响应的用户信息map
                auto file_map = resp.mutable_file_data();        // 这是批量文件请求响应中的map
                UserInfo info;
                info.set_user_id(user->user_id());
                info.set_nickname(user->nickname());
                info.set_description(user->description());
                info.set_phone(user->phone());
                // 防止下面那行不能运行
                // auto file_download_data = file_map[user->avatar_id()];
                // info.set_avatar(file_download_data.file_content());
                info.set_avatar((*file_map)[user->avatar_id()].file_content()); // 直接从文件响应哈希表中根据avatar_id获取即可
                (*user_map)[user->user_id()] = info;
            }
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

        void SetUserAvatar(google::protobuf::RpcController *controller,
                           const ::bite_im::SetUserAvatarReq *request,
                           ::bite_im::SetUserAvatarRsp *response,
                           ::google::protobuf::Closure *done)
        {
            // 错误函数---lambda
            auto err_response_func = [this, response](const std::string &request_id,
                                                      const std::string &errmsg) -> void
            {
                response->set_request_id(request_id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                // 无法登陆不用设置保存code_id
                return;
            };
            // 管理rpc对象
            brpc::ClosureGuard rpc_guard(done);

            // 1.检测用户是否存在
            std::string user_id = request->user_id();
            auto user = _mysql_user->select_by_id(user_id);
            if (!user) // 表示不存在该用户
            {
                LOG_ERROR("用户[{}]不存在，请求ID[{}]", user_id, request->request_id());
                return err_response_func(request->request_id(), "提供用户id无法找到用户");
            }

            // 2.获取文件管理子服务相关结构
            auto channel = _service_manager->choose(_file_service_name);
            if (!channel) // 服务不存在
            {
                LOG_ERROR("文件子服务[{}]节点未找到，请求ID[{}]", _file_service_name, request->request_id());
                return err_response_func(request->request_id(), "未找到文件子服务管理节点");
            }

            bite_im::FileService_Stub stub(channel.get());
            bite_im::PutSingleFileReq req;
            req.set_request_id(request->request_id());
            // 2.组织头像上传请求
            std::string file_name = UUID::uuid();
            req.mutable_file_data()->set_file_name(file_name);
            req.mutable_file_data()->set_file_size(request->avatar().size()); // 从request中获取指定头像数据
            req.mutable_file_data()->set_file_content(request->avatar());     // 此时客户端就能读到这个数据获取到头像
            bite_im::PutSingleFileRsp resp;
            // 获取一个rpc操作对象
            brpc::Controller cntl;
            // stub调用我们实现的服务类ipml的接口
            stub.PutSingleFile(&cntl, &req, &resp, nullptr);
            if (cntl.Failed() == true || resp.success() == false)
            {
                LOG_ERROR("文件子服务调用失败，请求ID[{}]，原因[{}]", request->request_id(), cntl.ErrorText());
                return err_response_func(request->request_id(), "文件子服务调用失败");
            }
            std::string avatar_id = resp.file_info().file_id(); // 获得头像id
            // 3.更新数据库中的avatar
            user->avatar_id(avatar_id);
            if (_mysql_user->update(user) == false)
            {
                LOG_ERROR("更新用户[{}]数据失败，请求ID[{}]", user->user_id(), request->request_id());
                return err_response_func(request->request_id(), "数据库更新头像id失败");
            }
            // 4.更新es搜索引擎中的用户信息
            if (_es_user->appendData(user->user_id(), user->phone(),
                                     user->nickname(), user->description(), avatar_id) == false)
            {
                LOG_ERROR("头像id更新ES搜索引擎新失败，请求ID[{}]", request->request_id());
                return err_response_func(request->request_id(), "es搜索引擎更新头像id失败");
            }
            // 5.组织响应
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

        void SetUserNickname(google::protobuf::RpcController *controller,
                             const ::bite_im::SetUserNicknameReq *request,
                             ::bite_im::SetUserNicknameRsp *response,
                             ::google::protobuf::Closure *done)
        {
            // 错误函数---lambda
            auto err_response_func = [this, response](const std::string &request_id,
                                                      const std::string &errmsg) -> void
            {
                response->set_request_id(request_id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                // 无法登陆不用设置保存code_id
                return;
            };
            // 管理rpc对象
            brpc::ClosureGuard rpc_guard(done);
            // 1.检测用户是否存在
            std::string user_id = request->user_id();
            auto user = _mysql_user->select_by_id(user_id);
            if (!user) // 表示不存在该用户
            {
                LOG_ERROR("用户[{}]不存在，请求ID[{}]", user_id, request->request_id());
                return err_response_func(request->request_id(), "提供用户id无法找到用户");
            }
            // 2.差错检验
            std::string nickname = request->nickname();
            if (check_nickname(nickname) == false)
            {
                return err_response_func(request->request_id(), "用户名格式不合法");
            }

            // 3.将nickname插入数据库中
            user->nickname(nickname);
            if (_mysql_user->update(user) == false)
            {
                LOG_ERROR("更新用户[{}]数据失败，请求ID[{}]", user->user_id(), request->request_id());
                return err_response_func(request->request_id(), "数据库更新昵称失败");
            }
            // 4.更新es搜索引擎中的用户信息
            if (_es_user->appendData(user->user_id(), user->phone(),
                                     nickname, user->description(), user->avatar_id()) == false)
            {
                LOG_ERROR("昵称更新ES搜索引擎新失败，请求ID[{}]", request->request_id());
                return err_response_func(request->request_id(), "es搜索引擎更新昵称失败");
            }
            // 5.组织响应
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

        void SetUserDescription(google::protobuf::RpcController *controller,
                                const ::bite_im::SetUserDescriptionReq *request,
                                ::bite_im::SetUserDescriptionRsp *response,
                                ::google::protobuf::Closure *done)
        {
            // 错误函数---lambda
            auto err_response_func = [this, response](const std::string &request_id,
                                                      const std::string &errmsg) -> void
            {
                response->set_request_id(request_id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                // 无法登陆不用设置保存code_id
                return;
            };
            // 管理rpc对象
            brpc::ClosureGuard rpc_guard(done);
            // 1.检测用户是否存在
            std::string user_id = request->user_id();
            auto user = _mysql_user->select_by_id(user_id);
            if (!user) // 表示不存在该用户
            {
                LOG_ERROR("用户[{}]不存在，请求ID[{}]", user_id, request->request_id());
                return err_response_func(request->request_id(), "提供用户id无法找到用户");
            }
            // 2.获取签名
            std::string description = request->description();

            // 3.将nickname插入数据库中
            user->description(description);
            if (_mysql_user->update(user) == false)
            {
                LOG_ERROR("更新用户[{}]描述数据失败，请求ID[{}]", user->user_id(), request->request_id());
                return err_response_func(request->request_id(), "数据库更新用户描述失败");
            }
            // 4.更新es搜索引擎中的用户信息
            if (_es_user->appendData(user->user_id(), user->phone(),
                                     user->nickname(), description, user->avatar_id()) == false)
            {
                LOG_ERROR("用户描述更新ES搜索引擎新失败，请求ID[{}]", request->request_id());
                return err_response_func(request->request_id(), "es搜索引擎更新用户描述失败");
            }
            // 5.组织响应
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

        void SetUserPhoneNumber(google::protobuf::RpcController *controller,
                                const ::bite_im::SetUserPhoneNumberReq *request,
                                ::bite_im::SetUserPhoneNumberRsp *response,
                                ::google::protobuf::Closure *done)
        {
            // 错误函数---lambda
            auto err_response_func = [this, response](const std::string &request_id,
                                                      const std::string &errmsg) -> void
            {
                response->set_request_id(request_id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                // 无法登陆不用设置保存code_id
                return;
            };
            // 管理rpc对象
            brpc::ClosureGuard rpc_guard(done);
            // 1.检测用户是否存在
            std::string user_id = request->user_id();
            auto user = _mysql_user->select_by_id(user_id);
            if (!user) // 表示不存在该用户
            {
                LOG_ERROR("用户[{}]不存在，请求ID[{}]", user_id, request->request_id());
                return err_response_func(request->request_id(), "提供用户id无法找到用户");
            }
            // 2.差错检验
            std::string phone = request->phone_number();
            if (check_phone(phone) == false)
            {
                return err_response_func(request->request_id(), "手机号格式不合法");
            }

            // 3.检验验证码
            std::string code_id = request->phone_verify_code_id();
            std::string code = request->phone_verify_code();
            std::string redis_code = *(_redis_codes->code(code_id));
            if (redis_code != code)
            {
                // 如果报错可能是 *(_redis_codes->code(code_id));解引用的问题
                LOG_ERROR("验证码[{}]-[{}]不匹配，请求ID[{}]", redis_code, code, request->request_id());
                return err_response_func(request->request_id(), "验证码错误");
            }
            // 4.将phone插入数据库中
            user->phone(phone);
            if (_mysql_user->update(user) == false)
            {
                LOG_ERROR("更新用户[{}]数据失败，请求ID[{}]", user->user_id(), request->request_id());
                return err_response_func(request->request_id(), "数据库更新手机号失败");
            }
            // 5.更新es搜索引擎中的用户信息
            if (_es_user->appendData(user->user_id(), phone,
                                     user->nickname(), user->description(), user->avatar_id()) == false)
            {
                LOG_ERROR("手机号更新ES搜索引擎新失败，请求ID[{}]", request->request_id());
                return err_response_func(request->request_id(), "es搜索引擎更新手机号失败");
            }
            // 6.组织响应
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

    private:
        DMSClient::ptr _dms_client;
        // es存储
        ESUser::ptr _es_user;
        // mysql存储
        UserTable::ptr _mysql_user;
        // redis存储
        Session::ptr _redis_session;
        Status::ptr _redis_status;
        Codes::ptr _redis_codes;

        // rpc获取文件存储服务信道
        std::string _file_service_name;
        ServiceManager::ptr _service_manager;
    };

    class UserServer
    {
        using RpcServerPtr = std::shared_ptr<brpc::Server>;

    public:
        using ptr = std::shared_ptr<UserServer>;
        UserServer(const Discovery::ptr &dis_client,
                   const Registry::ptr &reg_client,
                   const RpcServerPtr &rpc_server,
                   const ElasticPtr &es_client,
                   const DBPtr &mysql_client,
                   const RedisPtr &redis_client)
            : _dis_client(dis_client),
              _reg_client(reg_client),
              _rpc_server(rpc_server),
              _es_client(es_client),
              _mysql_client(mysql_client),
              _redis_client(redis_client) {}
        ~UserServer() {}

        // 启动RPC服务器
        void start()
        {
            _rpc_server->RunUntilAskedToQuit();
        }

    private:
        // 项目运行期间都需要持续维护这些对象
        ElasticPtr _es_client;
        DBPtr _mysql_client;
        RedisPtr _redis_client;
        Discovery::ptr _dis_client;
        Registry::ptr _reg_client;
        RpcServerPtr _rpc_server;
    };

    /*建造者设计模式，封装对象的构造过程，防止用户在进行操作时，对未构造的对象进行操作*/

    class UserServerBuilder
    {
        using RpcServerPtr = std::shared_ptr<brpc::Server>;

    public:
        void make_dms_object(const std::string &access_key_id, const std::string &access_key_secret)
        {
            _dms_client = std::make_shared<DMSClient>(access_key_id, access_key_secret);
        }
        void make_redis_object(const std::string &redis_ip, int redis_port, bool keep_alive, int db_flag)
        {
            _redis_client = RedisFactory::create(redis_ip, redis_port, keep_alive, db_flag);
        }
        void make_mysql_object(const std::string &mysql_user_name,
                               const std::string &mysql_password,
                               const std::string &db_name,
                               const std::string &mysql_host,
                               int mysql_port,
                               const std::string &cset,
                               int conn_pool_num)
        {
            _mysql_client = ODBFactory::create(mysql_user_name, mysql_password, db_name,
                                               mysql_host, mysql_port, cset, conn_pool_num);
        }
        void make_es_object(const std::vector<std::string> &es_host_list)
        {
            _es_client = ElasticFactory::create(es_host_list);
        }
        // 构造服务发现和服务管理客户端
        void make_find_object(const std::string &reg_host,
                              const std::string &base_service_name,
                              const std::string &file_service_name)
        {
            _file_service_name = file_service_name;
            _service_manager = std::make_shared<ServiceManager>();
            _service_manager->declareService(file_service_name);
            LOG_INFO("在用户服务中添加了文件管理子服务[{}]", file_service_name);
            auto put_cb = std::bind(&bite_im::ServiceManager::onServiceOnline, _service_manager.get(),
                                    std::placeholders::_1, std::placeholders::_2);
            auto del_cb = std::bind(&bite_im::ServiceManager::onServiceOffLine, _service_manager.get(),
                                    std::placeholders::_1, std::placeholders::_2);
            _dis_client = std::make_shared<Discovery>(base_service_name, reg_host, put_cb, del_cb);
        }
        // 构造服务注册客户端对象
        void make_reg_object(const std::string &reg_host,
                             const std::string &service_name,
                             const std::string &access_host)
        {
            _reg_client = std::make_shared<Registry>(reg_host);
            _reg_client->registry(service_name, access_host);
        }
        // 搭建rpc服务器
        void make_rpc_server(uint16_t port, int32_t time_out, uint8_t num_threads)
        {
            // 防止未完全构造所需对象
            if (_dms_client.get() == nullptr)
            {
                LOG_ERROR("服务器建造失败，未初始化短信发送模块");
                abort();
            }
            if (_es_client.get() == nullptr)
            {
                LOG_ERROR("服务器建造失败，未初始化es客户端模块");
                abort();
            }
            if (_mysql_client.get() == nullptr)
            {
                LOG_ERROR("服务器建造失败，未初始化mysql客户端模块");
                abort();
            }
            if (_redis_client.get() == nullptr)
            {
                LOG_ERROR("服务器建造失败，未初始化redis模块");
                abort();
            }
            if (_service_manager.get() == nullptr)
            {
                LOG_ERROR("服务器建造失败，未初始化服务管理模块");
                abort();
            }

            // 创建rpc服务器对象
            _rpc_server = std::make_shared<brpc::Server>();

            // 构建服务，并添加服务
            UserServiceImpl *user_service = new UserServiceImpl(_dms_client, _es_client, _mysql_client, _redis_client,
                                                                _service_manager, _file_service_name); // 注意这里需要在堆上开辟空间
            int ret = _rpc_server->AddService(user_service, brpc::ServiceOwnership::SERVER_OWNS_SERVICE);
            if (ret == -1)
            {
                LOG_ERROR("rpc服务器添加服务失败");
                abort();
            }

            auto options = std::make_shared<brpc::ServerOptions>();
            // 启动服务器
            options->idle_timeout_sec = time_out; // 连接空闲超时时间
            options->num_threads = num_threads;
            ret = _rpc_server->Start(port, options.get());
            if (ret == -1)
            {
                LOG_ERROR("服务器启动失败");
                abort();
            }
        }

        UserServer::ptr build()
        {
            // 检测维护对象是否建立
            if (_dis_client.get() == nullptr)
            {
                LOG_ERROR("服务器建造失败，未初始化服务发现模块");
                abort();
            }
            if (_reg_client.get() == nullptr)
            {
                LOG_ERROR("服务器建造失败，未初始化服务注册模块");
                abort();
            }
            if (_rpc_server.get() == nullptr)
            {
                LOG_ERROR("服务器建造失败，未搭建Rpc服务器");
                abort();
            }

            UserServer::ptr server = std::make_shared<UserServer>(_dis_client, _reg_client, _rpc_server,
                                                                  _es_client, _mysql_client, _redis_client);
            return server;
        }

    private:
        // 数据管理模块
        ElasticPtr _es_client;
        DBPtr _mysql_client;
        RedisPtr _redis_client;

        // 短信验证码
        DMSClient::ptr _dms_client;

        // 服务注册和发现模块
        std::string _file_service_name;
        ServiceManager::ptr _service_manager;
        Discovery::ptr _dis_client;
        Registry::ptr _reg_client;
        // rpc服务器
        RpcServerPtr _rpc_server;
    };
}